package com.sys.common.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.springframework.stereotype.Component;

/**
 * 文件的工具类
 * @author 朱明宇
 */
@Component("fileUtil")
public class FileUtil
{
	/*匹配${}的正则,还有分组的概念*/
	private Pattern escapresource = Pattern.compile("(\\$\\{)([\\w]+)(\\})");
	
	/**
	 * 读取一个文件内容
	 * @param is 文件的输入流
	 * @return 文件的内容
	 */
	public String readFile(InputStream is)
	{
		StringBuffer stringBuffer = new StringBuffer() ;
		try
		{
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
			String line = "" ; 
			while((line = bufferedReader.readLine()) != null)
			{
				/*System.out.println(line);*/
				stringBuffer.append(line + "\r\n");
			}
		} catch (Exception e)
		{
			e.printStackTrace();
		}
		return stringBuffer.toString() ; 
	}
	
	/**
	 * 专门用来处理点位符
	 * @return
	 */
	public String replaceOperator(String source , Map<String, String> paramsMap)
	{
		if(paramsMap.size() == 0 )
		{
			return source ; 
		}
		
		StringBuffer sb = new StringBuffer();
		/*将${朱明宇}的值替换掉*/
		Matcher matcher = this.escapresource.matcher(source);
		while(matcher.find())
		{
			if(paramsMap.get(matcher.group(2)) != null )
			{
				matcher.appendReplacement(sb, paramsMap.get(matcher.group(2))) ;
			}
		}
		
		/* 将尾巴加上去 */
		matcher.appendTail(sb);
		return sb.toString() ; 
	}
	
	/**
	 * 读取某个文件的内容
	 * 并且为对象赋值,类似于资源文件
	 * 如${朱明宇},map中键为朱明宇的值放的是yanglp,会将整个${朱明宇}替换为yanglp
	 * 
	 * @param sourceFile
	 * @param charset
	 * @param paramsMap 键为源文件中出现的,值为真正要替换的值
	 * @return
	 */
	public String readFile(InputStream is , String charset ,
			Map<String, String> paramsMap)
	{
		if(charset == null || "".equalsIgnoreCase(charset))
		{
			charset = ConstatFinalUtil.CHARSET ; 
		}
		StringBuffer sb = new StringBuffer();
		try
		{
			BufferedReader br = new BufferedReader(new InputStreamReader(is, charset));
			String line = "" ; 
			StringBuffer patressb = new StringBuffer();
			while((line = br.readLine()) != null )
			{
				line = line.trim() ; 
				patressb.append(line);
			}
			sb.append(this.replaceOperator(patressb.toString(), paramsMap));
			br.close();
		} catch (IOException e)
		{
			ConstatFinalUtil.SYS_LOGGER.error("读取文件出错了", e);
		}
		return sb.toString() ; 
	}
	
	/**
	 * 拷贝文件
	 */
	public boolean copyFile(InputStream is, OutputStream os)
	{
		try
		{
			byte[] b = new byte[1024];
			int len = 0 ; 
			while((len = is.read(b)) != -1)
			{
				os.write(b, 0, len);
			}
			return true ; 
		} catch (IOException e)
		{
			e.printStackTrace();
		}finally
		{
			/* 一定要分开捕获 */
			try
			{
				os.close();
			} catch (IOException e)
			{
				e.printStackTrace();
			}
			try
			{
				is.close();
			} catch (IOException e)
			{
				e.printStackTrace();
			}
		}
		/* 拷贝失败了 */
		return false ; 
	}
	
	/**
	 * 拷贝文件
	 * @param is 输入流
	 * @param os 输出流
	 * @param closeFlag 是否关闭流
	 * @return 拷贝的结果
	 */
	public boolean copyFile(InputStream is , OutputStream os , boolean closeFlag)
	{
		/* 拷贝的时候,带上粗管子 */
		BufferedInputStream bis = new BufferedInputStream(is);
		BufferedOutputStream bos = new BufferedOutputStream(os);
		
		/* 准备容器
		 * 1024 * 1024 * 2 === 2m
		 *  */
		byte[] b = new byte[2 * 1024 * 1024];
		int len = 0 ;
		try
		{
			while((len = bis.read(b)) != -1)
			{
				bos.write(b, 0, len);
			}
			/* 写数据缓存 */
			bos.flush();
			/* 拷贝完成,返回true */
			return true ; 
		} catch (IOException e)
		{
			e.printStackTrace();
		}finally
		{
			if(closeFlag)
			{
				try
				{
					if(is != null)
					{
						is.close();
						is = null; 
					}
				} catch (IOException e)
				{
					e.printStackTrace();
				}
				try
				{
					if(os != null)
					{
						os.close();
						os = null ;
					}
				} catch (IOException e)
				{
					e.printStackTrace();
				}
			}
		}
		return false ; 
	}
	
	/**
	 * 关闭流
	 */
	public void closeStreamAll(InputStream is,OutputStream os)
	{
		try
		{
			if(is != null)
			{
				is.close();
				is = null; 
			}
		} catch (IOException e)
		{
			ConstatFinalUtil.SYS_LOGGER.error("关闭is异常",e);
		}
		try
		{
			if(os != null)
			{
				os.close();
				os = null ;
			}
		} catch (IOException e)
		{
			ConstatFinalUtil.SYS_LOGGER.error("关闭os异常",e);
		}
	}
	
	/**
	 * 关闭流
	 */
	public void closeCharAll(Reader reader,Writer writer)
	{
		try
		{
			if(reader != null)
			{
				reader.close();
				reader = null; 
			}
		} catch (IOException e)
		{
			ConstatFinalUtil.SYS_LOGGER.error("关闭is异常",e);
		}
		try
		{
			if(writer != null)
			{
				writer.close();
				writer = null ;
			}
		} catch (IOException e)
		{
			ConstatFinalUtil.SYS_LOGGER.error("关闭os异常",e);
		}
	}
	
	/**
	 * 树形结构
	 * @param file 给定的目录或者文件
	 * @return 返回一个容器,里面放的是Map;<file对象,level层级,treeName树形结构的名字>
	 */
	public List<Map<String, Object>> folderTree(File file, boolean deleteFlag)
	{
		List<Map<String, Object>> fileResList = new ArrayList<Map<String, Object>>();
		/* 递归 */
		folderTreeUtil(file,0,fileResList,deleteFlag,file.getAbsolutePath()) ;
		return fileResList ; 
	}
	
	/**
	 * 主要是用来递归
	 * @param file
	 * @param fileResList
	 */
	private void folderTreeUtil(File file,int level, List<Map<String, Object>> fileResList,boolean deleteFlag,String parentAbsPath)
	{
		StringBuffer sb = new StringBuffer() ;
		for (int i = 0; i < level; i++)
		{
			sb.append(ConstatFinalUtil.SPLIT_STR);
		}
		//fileResList.add(file);
		Map<String, Object> fileMap = new HashMap<String, Object>();
		String fileAbsPath = file.getAbsolutePath() ; 
		String fileRelaName = fileAbsPath.substring(fileAbsPath.indexOf(parentAbsPath) + parentAbsPath.length(), fileAbsPath.length()) ; 
		
		fileMap.put("file", file);
		fileMap.put("fileRelaName", fileRelaName);
		fileMap.put("level", level);
		fileMap.put("treeName", sb.toString() + file.getName());
		fileMap.put("fileSize", file.length());
		if(fileRelaName.lastIndexOf(".") != -1)
		{
			fileMap.put("extendName", fileRelaName.substring(fileRelaName.lastIndexOf(".") + 1,fileRelaName.length()));
		}
		
		if(file.isDirectory())
		{
			/* 如果是目录的话,才会继续 */
			File[] listFiles = file.listFiles();
			for (File fileTemp : listFiles)
			{
				folderTreeUtil(fileTemp, level + 1 , fileResList , deleteFlag,parentAbsPath);
			}
		}
		
		/* 删除文件 */
		if(deleteFlag)
		{
			boolean deleteFlagTemp = file.delete();  
			fileMap.put("deleteFlag", deleteFlagTemp);
		}
		
		fileResList.add(fileMap);
	}
	
	/**
	 * 统计代码,参数为一个压缩文件
	 * @param zipFile
	 * @return
	 */
	public List<Map<String, Object>> codeStatJavaZipFile(File zipFile,File tarFile)
	{
//		if(!tarFile.exists())
//		{
//			tarFile.mkdirs();
//		}
		List<Map<String, Object>> codeStatList = new ArrayList<Map<String, Object>>();
		boolean unzip = this.unzip(zipFile, tarFile);
		if(unzip)
		{
			/* 解压成功 */
			codeStatList = this.codeStatJavaFolder(tarFile);
		}
		/* 删除目标目录 */
		this.folderTree(tarFile, true);
		tarFile.delete();
		return codeStatList ; 
	}
	
	/**
	 * 统计代码,参数为目录
	 * @param file
	 * @return
	 */
	public List<Map<String, Object>> codeStatJavaFolder(File file)
	{
		if(!file.isDirectory())
		{
			/* 文件不存在,或者文件是目录 */
			return Collections.EMPTY_LIST ; 
		}
		/* 先列出这个目录下面所有的目录和文件 */
		List<Map<String, Object>> folderTree = this.folderTree(file, false);
		for (Iterator iterator = folderTree.iterator(); iterator.hasNext();)
		{
			Map<String, Object> map = (Map<String, Object>) iterator.next();
			/* 获取文件对象 */
			File fileTemp = (File) map.get("file");
			if(fileTemp.isFile())
			{
				/* 如果是文件,进行代码统计 */
				Map<String, Object> codeStatJavaMap = this.codeStatJava(fileTemp);
				/* 将代码统计部分全部放到原来的map中 */
				map.putAll(codeStatJavaMap);
			}
		}
		return folderTree ; 
	}

	/**
	 * 代码统计--单个文件
	 * @param file 输入文件
	 * @return 返回结果;共多少行,代码多少行,单行注释多少行,多行注释多少行;空行多少个
	 */
	public Map<String, Object> codeStatJava(File file)
	{
		Map<String, Object> resultMap = new HashMap<String, Object>();
		if(file.isDirectory() || !file.exists())
		{
			/* 文件不存在,或者文件是目录 */
			return resultMap ; 
		}
		
		boolean extensionFlag = false ; 
		/**
		 * 只能处理java文件或者scala文件
		 */
		String extensionCodestat = ConstatFinalUtil.CONFIG_JSON.getString("extension.codestat");
		String[] extensionCodestats = extensionCodestat.split(",");
		for (int i = 0; i < extensionCodestats.length; i++)
		{
			String extensionTemp = extensionCodestats[i];
			if(file.getName().endsWith(extensionTemp))
			{
				extensionFlag = true ; 
				break ; 
			}
		}
		if(extensionFlag)
		{
			
		}else
		{
			return resultMap ; 
		}
		
		/* 定义一些常量
		 *  */
		/* 总行数 */
		int totalCount = 0 ; 
		/* 有效代码行数 */
		int codeCount = 0 ; 
		/* 单行注释 */
		int singleCommentCount = 0 ; 
		/* 多行注释 */
		int multiCommentCount = 0 ; 
		/* 空行 */
		int blankCount = 0 ; 
		FileInputStream fis = null ;
		try
		{
			/* 创建一个输入流 */
			fis = new FileInputStream(file);
			/* 读取文件内容 */
			String fileContent = this.readFile(fis);
			/* 拆分文件的内容 */
			String[] lines = fileContent.split("\r\n");
			/* 多行注释的标记 */
			boolean multiFlag = false ;
			for (String line : lines)
			{
				//System.out.println("======测试======" + line);
				/* 判断空行 */
				line = line.trim() ; 
				if("".equalsIgnoreCase(line) || "//".equalsIgnoreCase(line))
				{
					blankCount ++ ; 
				}else if(line.startsWith("//") && !multiFlag)
				{
					/* 单行注释 */
					singleCommentCount ++ ; 
				}else if(line.startsWith("/*"))
				{
					/* 多行注释开始 */
					multiCommentCount ++ ; 
					multiFlag = true ;
				}else if(line.endsWith("*/"))
				{
					/* 多行注释结束 */
					multiFlag = false ;
					multiCommentCount ++ ;
				}else if(multiFlag)
				{
					/* 多行注释中间的部分 */
					multiCommentCount ++ ; 
				}else 
				{
					/* 有效代码行 */
					codeCount ++ ; 
				}
				totalCount ++ ; 
			}
		} catch (FileNotFoundException e)
		{
			ConstatFinalUtil.SYS_LOGGER.error("操作失败了;文件路径:{}",file.getAbsoluteFile() , e);
		}finally
		{
			try
			{
				if(fis != null)
				{
					fis.close();
					fis = null ;
				}
			} catch (IOException e)
			{
				ConstatFinalUtil.SYS_LOGGER.error("关闭文件失败了;路径:{}",file.getAbsolutePath(),e);
			}
		}
		resultMap.put("totalCount", totalCount);
		resultMap.put("codeCount", codeCount);
		resultMap.put("singleCommentCount", singleCommentCount);
		resultMap.put("multiCommentCount", multiCommentCount);
		resultMap.put("blankCount", blankCount);
		/* 所有有可能的代码加起来是否等于总和 */
		int totalCountRes = codeCount + singleCommentCount + multiCommentCount + blankCount ; 
		resultMap.put("equals", totalCount == totalCountRes);
		resultMap.put("name", file.getName());
		return resultMap ; 
	}
	
	/**
	 * 压缩
	 * @param os 压缩文件的输出流
	 * @param souFileMap 要压缩的文件列表;键为压缩包中文件的名字,值为:输入流(要压缩的文件)
	 */
	public boolean zip(OutputStream os,Map<String, InputStream> souFileMap)
	{
		ZipOutputStream zos = new ZipOutputStream(os);
		try
		{
			/* 循环取出压缩文件列表 */
			for (Iterator<Entry<String, InputStream>> iterator = souFileMap.entrySet().iterator(); iterator.hasNext();)
			{
				Entry<String, InputStream> entry = iterator.next();
				String key = entry.getKey() ; 
				InputStream souFileIs = entry.getValue() ; 
				
				/* 压缩包里面的选项 */
				ZipEntry zipEntry = new ZipEntry(key);
				/* 将压缩包里面的内容放到压缩包中 */
				zos.putNextEntry(zipEntry);
				
				/* 拷贝文件 */
				this.copyFile(souFileIs, zos,false);
				/* 关闭文件 */
				souFileIs.close();
				/* 此文件结束 */
				zos.closeEntry();
				ConstatFinalUtil.SYS_LOGGER.info("==zip==name:{}=",key);
			}
			return true ; 
		} catch (FileNotFoundException e)
		{
			ConstatFinalUtil.SYS_LOGGER.error("文件木有找到;", e);
		} catch (Exception e)
		{
			ConstatFinalUtil.SYS_LOGGER.error("读取文件出错了", e);
		}finally
		{
			try
			{
				if(zos != null)
				{
					zos.close();
					zos = null ; 
				}
			} catch (IOException e)
			{
				ConstatFinalUtil.SYS_LOGGER.error("关闭zos出错了", e);
			}
		}
		return false ; 
	}
	
	/**
	 * 解压缩
	 * @param is 压缩文件的位置
	 * @param file 解压缩的目录
	 */
	public boolean unzip(File souFile,File tarFile)
	{
		if(tarFile.exists() || tarFile.isFile())
		{
			return false ; 
		}
		if(!tarFile.exists())
		{
			tarFile.mkdirs();
		}
		/* 创建一个压缩文件 */
		ZipFile zipFile = null ; 
		try
		{
			zipFile = new ZipFile(souFile);
			/**
			 * 获取压缩文件的枚举项
			 */
			Enumeration enu = zipFile.entries() ; 
			while(enu.hasMoreElements())
			{
				ZipEntry zipEntry = (ZipEntry) enu.nextElement() ; 
				/* 压缩包中的文件对象 */
				File zipTempFile = new File(tarFile , zipEntry.getName());
				/* 判断是目录还是文件 */
				if(zipEntry.isDirectory())
				{
					/* 创建目录 */
					zipTempFile.mkdirs() ; 
				}else
				{
					/* 只有一层目录的时候会有问题,会识别为文件 */
					if(!zipTempFile.getParentFile().exists())
					{
						zipTempFile.getParentFile().mkdirs();
					}
					/* 文件 */
					InputStream is = zipFile.getInputStream(zipEntry); 
					OutputStream os = new FileOutputStream(zipTempFile);
					this.copyFile(is, os);
				}
			}
			return true ; 
		} catch (Exception e)
		{
			ConstatFinalUtil.SYS_LOGGER.error("读取文件出错了", e);
		}finally
		{
			try
			{
				if(zipFile != null)
				{
					zipFile.close();
					zipFile = null ; 
				}
			} catch (IOException e)
			{
				ConstatFinalUtil.SYS_LOGGER.error("关闭zos出错了", e);
			}
		}
		return false ; 
	}
	
	public static void main(String[] args)
	{
		FileUtil fileUtil = new FileUtil() ; 
		String source = "abc${test}de${aa}fg" ;
		Map<String, String> paramsMap = new HashMap<String, String>() ;
		paramsMap.put("test", "测试一下");
		paramsMap.put("aa", "测试二下");
		String result = fileUtil.replaceOperator(source, paramsMap);
		System.out.println(result);
	}
}
